/***************************************************
 log sync 抓取服务器上的文件
 执行流程：
 1、在服务器上生成列表文件(shell)
 2、本程序抓取生成的列表文件
 3、对比老列表文件和新抓取的文件是否修改
 4、如果有修改则将要修改的文件列表post到服务器
 5、服务器接收到命令之后，执行zip将更新的文件打包，并返回success
 6、本程序抓回打包文件，并unzip到本地，完成更新
 shaomin.shi@
 ver:1.0
***************************************************/ 
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<algorithm>
#include<cstdlib>
#include<stdexcept>
#include"log_sync.h"
#include"clx/http.h"
#include"clx/uri.h"
#include"clx/unzip.h"
#include"clx/utility.h"
#include"clx/ini.h"
#include "clx/filesystem.h"
/***************************************************/
int main(int argc , char *argv[])
{
	if (argc < 2){
		std::cout<<"usage: log_sync ini_file"<<std::endl;
		std::exit(-1);
	}
	try{
		clx::ini ini(argv[1]);
		std::string ini_server_name,ini_host,ini_list_path,ini_post_cmd_path,ini_out_file,ini_password,ini_data_file_path;
		if (ini.exist("Settings", "SERVER")) {
			ini_server_name = ini["Settings"]["SERVER"];
		}else{
			std::cout<<"ini infomation [Settings][SERVER] not exist!" <<std::endl;
			std::exit(-1);
		}		
		if (ini.exist("Settings", "HOST")) {
			ini_host = ini["Settings"]["HOST"];
		}else{
			std::cout<<"ini infomation [Settings][HOST] not exist!" <<std::endl;
			std::exit(-1);
		}
		if (ini.exist("Settings", "FILE_LIST")) {
			ini_list_path = ini["Settings"]["FILE_LIST"];
		}else{
			std::cout<<"ini infomation [Settings][FILE_LIST] not exist!" <<std::endl;
			std::exit(-1);
		}	
		if (ini.exist("Settings", "CMD_PATH")) {
			ini_post_cmd_path = ini["Settings"]["CMD_PATH"];
		}else{
			std::cout<<"ini infomation [Settings][CMD_PATH] not exist!" <<std::endl;
			std::exit(-1);
		}	
		if (ini.exist("Settings", "OUT_FILE")) {
			ini_out_file = ini["Settings"]["OUT_FILE"];
		}else{
			std::cout<<"ini infomation [Settings][OUT_FILE] not exist!" <<std::endl;
			std::exit(-1);
		}	
		if (ini.exist("Settings", "PASSWORD")) {
			ini_password = ini["Settings"]["PASSWORD"];
		}else{
			std::cout<<"ini infomation [Settings][PASSWORD] not exist!" <<std::endl;
			std::exit(-1);
		}		
		if (ini.exist("Settings", "DATA_FILE_PATH")) {
			ini_data_file_path = ini["Settings"]["DATA_FILE_PATH"];
		}else{
			std::cout<<"ini infomation [Settings][DATA_FILE_PATH] not exist!" <<std::endl;
			std::exit(-1);
		}		
		std::string export_file = ini_list_path.substr(ini_list_path.rfind("/")+1);
		std::cout<<"***start download: "<< export_file <<std::endl;
		clx::http session(clx::uri::encode( ini_host ), HTTP_SERVER_PORT );
		session.get(clx::uri::encode( ini_list_path ));
		std::cout<<"http code:"<<session.code()<<std::endl;
		std::cout << "body size: " << session.body().size() << std::endl;
		/*save export file*/
		std::ofstream out( export_file.c_str() , std::ios::binary);
		if(out.is_open()){
			out<< session.body();
			out.close();		
		}
		std::cout<< "save : "<< export_file << " ok!"<<std::endl;
		//*读取上次文件*//
		std::string old_file_list = ini_server_name + "_file_list.log";
		std::cout<<"***start reading "<<old_file_list<<"..."<<std::endl;
		std::ifstream in( old_file_list.c_str() );		
		char buffer[MAX_LINE_SIZE];
		std::vector<std::string> vector_log,need_update_file,new_export_file;
		std::vector<std::string>::iterator it_log,it_new_export;
		if(in.is_open()){
			while (! in.eof() ) {
				in.getline(buffer , MAX_LINE_SIZE);
				vector_log.push_back(buffer);
			}
		}else{
			std::cout<<old_file_list<<" file not found!"<<std::endl;
		}
		in.close();
		//unzip 解压并找出大小、修改日期不同的文件，组装出zip命令
		clx::unzip uz(export_file , ini_password );
		for(clx::unzip::iterator pos=uz.begin(); pos!=uz.end(); ++pos ){
			std::cout <<"file(s) in "<<export_file<<" : "<< pos->path() << std::endl;
			std::string new_line,filename,line,filesize,datetime,old_filesize,old_datetime;
			while(true){
				if(!std::getline(*pos,new_line)) break;	
				new_export_file.push_back(new_line);
				size_t first_pos = new_line.find(" ");
				size_t last_pos  = new_line.find_last_of(" ");
				if(first_pos>0 && last_pos>0)
				{
					filesize = new_line.substr(0,first_pos);
					datetime = new_line.substr(first_pos+1,16);
					filename = new_line.substr(last_pos+1);	
					bool file_found = false;
					for(it_log=vector_log.begin();it_log!=vector_log.end();++it_log){
						line = *it_log;
						std::string old_file_name = line.substr(line.rfind(" ")+1);
						if(std::strcmp(old_file_name.c_str(),filename.c_str())==0)//找到该行
						{ 
							//std::cout<<old_file_name<<filename<<std::endl;
							file_found = true;
							if(new_line.compare(line)!=0){ //比较该行相同
								// std::cout<<"new:"<<new_line<<std::endl;
								// std::cout<<"old:"<<line<<std::endl;	
								need_update_file.push_back(filename);
							}
						}					
					}	
					if(file_found==false){ //新文件有，老文件没有
						//std::cout<<"new:"<<new_line<<std::endl;	
						need_update_file.push_back(filename);
					}
				}
			}
		}
		/*检查更新列表*/
		if(need_update_file.empty()){
			std::cout<<"0 file(s) update!"<<std::endl;
			return 0;
		}
		//生成zip命令
		size_t count =0,num=0;
		std::string query_str="command=";
		for(it_log=need_update_file.begin();it_log!=need_update_file.end();++it_log){
			query_str.append( *it_log );
			query_str.append( " ");
			count++;
			if((count%100==0) || (count==need_update_file.size())){//每100个文件执行一次打包
				num++;
				/***********************************************/
				std::cout<<"***start post update list index: "<< num <<" to server..."<<std::endl;
				//std::cout << "post query_str: " << query_str << std::endl;
				//POST到服务器，生成打包文件
				std::string path = ini_post_cmd_path;
				session.post(clx::uri::encode(path), query_str);		
				std::cout << "post code: " << session.code() << std::endl;
				std::cout << session.body() << std::endl;		
				//抓回打包文件并unzip
				std::string output_file = ini_out_file.substr(ini_out_file.rfind("/")+1);		
				if(session.body().find("result:success")!=std::string::npos)
				{
					sleep( 1 ); 
					std::cout<<"***downloading "<<output_file<<" index : "<< num <<" from server..."<<std::endl;
					std::cout<<clx::uri::encode( ini_out_file) <<std::endl;
					session.post(clx::uri::encode( ini_out_file ) ,"");
					std::cout<<"http code:"<<session.code()<<std::endl;
					/*save output file*/
					std::ofstream output( output_file.c_str() , std::ios::binary);
					if(output.is_open()){
						output<< session.body();
						output.close();		
					}
					std::cout<< "save "<<output_file<<" ok!"<<std::endl;
				}else{
					std::cout<< "post result error..."<<std::endl;
					return 0;
				}
				//解压文件
				std::cout<<"***start unzip "<<output_file<<"..."<<std::endl;
				clx::unzip uz_out("output.zip", ini_password); 
				for (clx::unzip::iterator pos = uz_out.begin(); pos != uz_out.end(); ++pos){
					std::string out_path = "/" + pos->path();
					std::string base_path = "/data/he/cli/data/result/";
					if(out_path.find(base_path)!=std::string::npos){
						out_path.replace(0,base_path.length(),ini_data_file_path);
						//文件目录不存在，则创建
						std::string _t_path = out_path.substr(0,out_path.rfind("/"));
						if(!clx::filesystem::is_directory(_t_path)){
							std::string cmd = "mkdir -p ";
							cmd.append(_t_path);
							std::system(cmd.c_str());
						}
					}
					std::ofstream uz_output( out_path.c_str() , std::ios::binary);
					if(uz_output.is_open()){
						std::string buf;
						while(true) {
							if (!std::getline(*pos, buf)) break;
							uz_output << buf << std::endl;
						}
						uz_output.close();		
					}
					std::cout<< "unzip " << out_path << " ok"<< std::endl;
				}
				/**********************************************/
				query_str="command=";
				
			}
		}		
		std::cout<<"***update loacl file done!"<<std::endl;	
		//更新老文件列表
		std::cout<<"***update "<<old_file_list<<"..."<<std::endl;
		std::ofstream out_new_export(old_file_list.c_str(),std::ios::binary);
		if(out_new_export.is_open()){
			for(it_new_export=new_export_file.begin();it_new_export!=new_export_file.end();++it_new_export){
				out_new_export<<*it_new_export<<std::endl;
			}
			out_new_export.close();	
		}		
	}
	catch( clx::socket_error& e)
	{
		std::cerr << e.what() << std::endl;
		std::exit(-1);
	}
	catch(clx::sockaddress_error& e)
	{
		std::cerr << e.what() << std::endl;
		std::exit(-1);
	}
	catch(std::runtime_error& e)
	{
		std::cerr << e.what() << std::endl;
		std::exit(-1);
	}
	return 0;
}


